package ae.pcfc.cldos.online.web.services.interceptors.aware;

import javax.interceptor.Interceptors;
import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.BeansException;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.context.request.WebRequestInterceptor;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.WebRequestHandlerInterceptorAdapter;
import org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping;

/**
 * Scans @Controller annotated classes for presense of EJB 3.0 @Interceptors annotation.
 * Retrieves the HandlerInterceptor instances from application-context and adds them to execution chain.  
 * 
 * @see http://karthikg.wordpress.com/2009/10/12/athandlerinterceptor-for-spring-mvc/
 * 
 * @author Sergiy
 */
public class HandlerInterceptorAnnotationAwareHandlerMapping extends DefaultAnnotationHandlerMapping {

	/**
	 * @see org.springframework.web.servlet.handler.AbstractHandlerMapping#getHandlerExecutionChain(java.lang.Object,
	 *      javax.servlet.http.HttpServletRequest)
	 */
	@Override
	protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
		HandlerExecutionChain chain = super.getHandlerExecutionChain(handler, request);
		addDetectedInterceptors(chain.getHandler().getClass(), chain);
		return chain;
	}

	/**
	 * Adds found interceptors to execution chain.   
	 * 
	 * @param handlerClass
	 * @param chain
	 */
	@SuppressWarnings("unchecked")
	protected void addDetectedInterceptors(@SuppressWarnings("rawtypes") Class handlerClass, HandlerExecutionChain chain) {
		Interceptors interceptorAnnot = AnnotationUtils.findAnnotation(handlerClass, Interceptors.class);
		if (interceptorAnnot != null) {
			@SuppressWarnings("rawtypes")
			Class[] interceptorClasses = interceptorAnnot.value();
			if (interceptorClasses != null) {
				for (@SuppressWarnings("rawtypes") Class interceptorClass : interceptorClasses) {
					if (!HandlerInterceptor.class.isAssignableFrom(interceptorClass) && !WebRequestInterceptor.class.isAssignableFrom(interceptorClass)) {
						throw new IllegalArgumentException(interceptorClass + " specified on " + handlerClass
								+ " does not implement " + HandlerInterceptor.class.getName());
					}
					/*
					 * Get required interceptor from application context.
					 */
					Object bean = null;
					try {
						bean = getApplicationContext().getBean(interceptorClass);
					} catch (BeansException e) {
						// ignore
					}					
					if (bean != null) {
						HandlerInterceptor handlerInterceptor = null;
						if (bean instanceof WebRequestInterceptor) {
							handlerInterceptor = new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) bean);	
						} else {
							handlerInterceptor = (HandlerInterceptor) bean; 
						}
						/*
						 * Add found interceptor to execution chain.
						 */
						chain.addInterceptor(handlerInterceptor);
					}
				}
			}
		}
	}
}
